﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Threading.Tasks;

using LedCenter.Utils;
using LetiLib.Data;
using System.Diagnostics;

namespace LedCenter.GUI
{
    public partial class ProgramEditorPanel : UserControl
    {
        #region | Initization |
        Point textPosition = new Point(0, 0);
        FontStyle fStyle = FontStyle.Regular;
        Color textColor = Color.Red;
        private const int THUMBNAIL_SIZE = 128;
        bool thumbnailMode = true; // true = program view
        // false thumbnail view

        public ProgramEditorPanel()
        {
            InitializeComponent();
            int width = Convert.ToInt32(txt_LedMatrixWidth.Text);
            int height = Convert.ToInt32(txt_LedMatrixHeight.Text);
            letiLed.Size = new Size(width, height);
        }
        
        public void setThumbnailMode(bool mode)
        { 
            thumbnailMode = mode; 
            if (thumbnailMode)
            {
                thumbnailListView.Visible = true;
                tabControl1.Visible = false;
                groupBox_Effect.Visible = false;
            }
            else
            {
                thumbnailListView.Visible = false;
                tabControl1.Visible = true;
                groupBox_Effect.Visible = true;
            }
            
        }
        
        public void LoadFontToComboxFont()
        {
            // Load all font from system to Combo box
            InstalledFontCollection colFonts = new InstalledFontCollection();
            FontFamily[] fontFamilies = colFonts.Families;
            for (int i = 0; i < fontFamilies.Length; i++)
                cbo_FontName.Items.Add(fontFamilies[i].Name);

            // set default font to Tahoma, size 10
            cbo_FontName.Text = "Tahoma";
            cbo_FontSize.Text = "10";
        }

        private void ProgramEditorPanel_Load(object sender, EventArgs e)
        {
            // Load fonts
            LoadFontToComboxFont();  
        }
        #endregion

        void updateLed()
        {
            try
            {

                // Set size for led matrix.
                int fixWidth = Convert.ToInt32(txt_LedMatrixWidth.Text.ToString());
                int fixHeight = Convert.ToInt32(txt_LedMatrixHeight.Text.ToString());

                // get text content = cbo_Effect.SelectedIndex;*/
                string textContent = txt_StringBytes.Text;
                // get font name
                string fontName = cbo_FontName.Text;
                // get font size
                int fontSize = 10;// default value
                // if we choose value from combobox then set new font size, overwise, use default value(10)
                if (cbo_FontSize.Text != "")
                {
                    fontSize = Convert.ToInt32(cbo_FontSize.Text);
                }

                TextProgram tProg = new TextProgram(0, textContent, textColor, fontName, fontSize, fStyle, textPosition);
                letiLed.initWithBitmap(ImageUtils.ResizeImage(tProg.CreateBitmapImage(),letiLed.Resolution.Width,letiLed.Resolution.Height));
            }
            catch (System.Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
            }
        }

        #region | textbox change events |
        private void txt_StringBytes_TextChanged(object sender, EventArgs e)
        {
            updateLed();
        }

        private void txt_LedMatrixWidth_TextChanged(object sender, EventArgs e)
        {
            // update size of led matrix
            int width = 1;
            try
            {
                width = Convert.ToInt32(txt_LedMatrixWidth.Text);
            }
            catch(Exception ex)
            {
                width = 1;
            }
            int height = 1;
            try
            {
                height = Convert.ToInt32(txt_LedMatrixHeight.Text);
            }
            catch (Exception ex)
            {
                height = 1;
            }
            letiLed.Resolution = new Size(width,height);
            this.Invalidate();
        }

        private void txtIndex_TextChanged(object sender, EventArgs e)
        {
            try
            {
                letiLed.Index = Convert.ToInt32(txtIndex.Text);
            }
            catch (System.Exception ex)
            {
                letiLed.Index = 1;
            }
            
        }

        #endregion
        
        private void btnLedSave_Click(object sender, EventArgs e)
        {
            // For test <<
            ///letiLed.saveToFile();
            // << end

            // Invoke Send data event
            OnSendData(e);
        }

        #region | send data delegate |
        // An event that clients can use to be notified whenever the
        // user click send data to devices.
        public event EventHandler SendData;
        // Invoke the SendData event; called whenever list changes:
        protected virtual void OnSendData(EventArgs e)
        {
            if (SendData != null)
                SendData(this, e);
        }
        #endregion

        #region | move button event |
        private void btn_UpOffSet_Click(object sender, EventArgs e)
        {
            textPosition.Y--;
            updateLed();
        }

        private void btn_DownOffSet_Click(object sender, EventArgs e)
        {
            textPosition.Y++;
            updateLed();
        }

        private void btn_LeftOffSet_Click(object sender, EventArgs e)
        {
            textPosition.X--;
            updateLed();
        }

        private void btn_RightOffSet_Click(object sender, EventArgs e)
        {
            textPosition.X++;
            updateLed();
        }

        private void chk_Filter_CheckedChanged(object sender, EventArgs e)
        {

        }

        private void chk_BoldFonStyte_CheckedChanged(object sender, EventArgs e)
        {
            if (chk_BoldFonStyte.Checked)
                fStyle |= FontStyle.Bold;
            else
                fStyle &= ~FontStyle.Bold;
            updateLed();
        }

        private void chk_ItalicFonStyte_CheckedChanged(object sender, EventArgs e)
        {
            if (chk_ItalicFonStyte.Checked)
                fStyle |= FontStyle.Italic;
            else
                fStyle &= ~FontStyle.Italic;
            updateLed();
        }

        private void chk_UnderlineFonStyte_CheckedChanged(object sender, EventArgs e)
        {
            if (chk_UnderlineFonStyte.Checked)
                fStyle |= FontStyle.Underline;
            else
                fStyle &= ~FontStyle.Underline;
            updateLed();
        }

        private void cbo_FontName_SelectedIndexChanged(object sender, EventArgs e)
        {
            updateLed();
        }

        private void cbo_FontSize_SelectedIndexChanged(object sender, EventArgs e)
        {
            updateLed();
        }
        #endregion

        private void chk_Color_CheckedChanged(object sender, EventArgs e)
        {
            if (chk_RedColor.Checked && chk_GreenColor.Checked)
                textColor = _colors[3];
            else if (chk_RedColor.Checked)
                textColor = _colors[1];
            else if (chk_GreenColor.Checked)
                textColor = _colors[2];
            else
                textColor = _colors[0];

            //txt_StringBytes.SelectionColor = textColor;

            //RtbToBitmap(txt_StringBytes).Save("rtb.bmp");
            //string a = txt_StringBytes.Rtf;
            //txt_StringBytes.SaveFile("a.rtf");
            updateLed();
        }

        /*
        public Bitmap RtbToBitmap(RichTextBox rtb)
        {
            rtb.Update();  // Ensure RTB fully painted
            // Create bitmap Object.
            Bitmap objBmpImage = new Bitmap(8, 8);
            // Create variables.
            int intWidth = 0;
            int intHeight = 0;

            // Create a graphics object to measure the text's width and height.
            Graphics objGraphics = Graphics.FromImage(objBmpImage);
            string textContent = txt_StringBytes.Text;
            string fontName = cbo_FontName.Text;
            int fontSize = 10;// default value
            if (cbo_FontSize.Text != "")
                fontSize = Convert.ToInt32(cbo_FontSize.Text);
            // Create the Font object for the image text drawing.
            Font objFont = new Font(fontName, fontSize, fStyle, System.Drawing.GraphicsUnit.Point);
            // This is where the bitmap size is determined.
            intWidth = (int)objGraphics.MeasureString(textContent, objFont).Width;
            intHeight = (int)objGraphics.MeasureString(textContent, objFont).Height;


            // Check if sImageText = null
            if (intWidth == 0 || intHeight == 0)
            {
                intHeight = 1;
                intWidth = 1;
            }

            // Create the bmpImage again with the correct size for the text and font.
            objBmpImage = new Bitmap(objBmpImage, letiLed.Resolution);
            //objBmpImage = new Bitmap(objBmpImage, new Size(intWidth, intHeight));
            // Add the colors to the new bitmap.
            objGraphics = Graphics.FromImage(objBmpImage);

            // Set Background color
            objGraphics.Clear(Color.White);

            // 
            objGraphics.SmoothingMode = SmoothingMode.AntiAlias;
            objGraphics.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit;

            // Draw string to Image
            //objGraphics.DrawString(stringToConvert, objFont, new SolidBrush(textColor), pos);

            objGraphics.CopyFromScreen(rtb.PointToScreen(Point.Empty), Point.Empty, new Size(intWidth,intHeight));
            // Flush
            objGraphics.Flush();


            return objBmpImage;
        }
        */
        
        #region | keyboard events |
        private void letiLed_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.Up:
                    textPosition.Y--;
                    break;
                case Keys.Down:
                    textPosition.Y++;
                    break;
                case Keys.Left:
                    textPosition.X--;
                    break;
                case Keys.Right:
                    textPosition.X++;
                    break;
            }
            updateLed();
        }
        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            switch (keyData)
            {
                case Keys.Up:
                    textPosition.Y--;
                    break;
                case Keys.Down:
                    textPosition.Y++;
                    break;
                case Keys.Left:
                    textPosition.X--;
                    break;
                case Keys.Right:
                    textPosition.X++;
                    break;
                default:
                    return base.ProcessCmdKey(ref msg, keyData);
            }
            updateLed();
            return true;
        }
        #endregion

        #region | Thumbnails |

        private void InitThumbnail(string[] pathes)
        {
            if (pathes.Length == 0) return;
            ImageList imageList = new ImageList();
            imageList.ImageSize = new Size(64, 64);
            imageList.ColorDepth = ColorDepth.Depth24Bit;
            Image curImage;

            foreach (string path in pathes)
            {
                curImage = Image.FromFile(path);
                images.Add(curImage);
                //Image img1 = ProcessInputImage(curImage);

                imageList.Images.Add(getThumbnaiImage(imageList.ImageSize.Width, curImage));
            }

            for (int j = 0; j < pathes.Length; j++)
            {
                this.thumbnailListView.Items.Add(Path.GetFileName(pathes[j]));
                this.thumbnailListView.Items[j].ImageIndex = j;
            }

            this.thumbnailListView.View = View.LargeIcon;
            this.thumbnailListView.LargeImageList = imageList;
        }

        private Image getThumbnaiImage(int width, Image img)
        {
            Image thumb = new Bitmap(width, width);
            Image tmp = null;

            if (img.Width < width && img.Height < width)
            {
                using (Graphics g = Graphics.FromImage(thumb))
                {
                    int xoffset = (int)((width - img.Width) / 2);
                    int yoffset = (int)((width - img.Height) / 2);
                    g.DrawImage(img, xoffset, yoffset, img.Width, img.Height);
                }
            }
            else
            {
                Image.GetThumbnailImageAbort myCallback = new
                    Image.GetThumbnailImageAbort(ThumbnailCallback);

                if (img.Width == img.Height)
                {
                    thumb = img.GetThumbnailImage(
                             width, width,
                             myCallback, IntPtr.Zero);
                }
                else
                {
                    int k = 0;
                    int xoffset = 0;
                    int yoffset = 0;

                    if (img.Width < img.Height)
                    {
                        k = (int)(width * img.Width / img.Height);
                        tmp = img.GetThumbnailImage(k, width, myCallback, IntPtr.Zero);
                        xoffset = (int)((width - k) / 2);

                    }

                    if (img.Width > img.Height)
                    {
                        k = (int)(width * img.Height / img.Width);
                        tmp = img.GetThumbnailImage(width, k, myCallback, IntPtr.Zero);
                        yoffset = (int)((width - k) / 2);
                    }

                    using (Graphics g = Graphics.FromImage(thumb))
                    {
                        g.DrawImage(tmp, xoffset, yoffset, tmp.Width, tmp.Height);
                    }
                }
            }

            /*
            using (Graphics g = Graphics.FromImage(thumb))
            {
                g.DrawRectangle(Pens.Green, 0, 0, thumb.Width - 1, thumb.Height - 1);
            }
            */
            return thumb;
        }

        public bool ThumbnailCallback()
        {
            return true;
        }

        #endregion

        private void btn_Browser_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = "Image Files(*.BMP;*.JPG;*.GIF;*.PNG)|*.BMP;*.JPG;*.GIF;*.PNG|All files (*.*)|*.*";
            ofd.FilterIndex = 1;
            ofd.Multiselect = false;
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                currentMode = PROGRAM_MODE.TEXT;
                LETILed.getByteFromBitmap((Bitmap)Bitmap.FromFile(ofd.FileName));
                letiLed.initWithBitmap((Bitmap)Bitmap.FromFile(ofd.FileName));
                //updateLed();
                /*
                images.Clear();
                InitThumbnail(ofd.FileNames);
                 * */
            }
        }
        public List<Image> images = new List<Image>();
        private void thumbnailListView_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (thumbnailListView.SelectedIndices.Count != 1) return;
            if (thumbnailListView.SelectedIndices[0] < 0 || 
                thumbnailListView.SelectedIndices[0] >= thumbnailListView.Items.Count) return;
            int selectedIndex = 0;
            selectedIndex = thumbnailListView.SelectedIndices[0];
            Image image = images[selectedIndex];
            //Bitmap bm = DistanceQuantization(image);
            letiLed.initWithBitmap(ImageUtils.ResizeImage(image,letiLed.Resolution.Width,letiLed.Resolution.Height));
            //letiLed.initWithBitmap(bm);
        }

        private Bitmap DistanceQuantization(Image image)
        {
            Bitmap orgImage = (Bitmap)image;
            Bitmap targetImage = new Bitmap(orgImage.Width,orgImage.Height);

            for (int j = 0; j < image.Height; j++)
            {
                for (int i = 0; i < image.Width; i++)
                {
                    targetImage.SetPixel(i, j, getBestColorMatch(orgImage.GetPixel(i,j)));
                }   
            }
            return targetImage;
        }
        Color[] _colors = new Color[]
        {
            Color.Black,
            Color.Red,
            //Color.FromArgb(255,0,255,0),
            Color.Lime,
            Color.Yellow
        };

        Color getBestColorMatch(Color pixel)
        {
            int leastDistance = int.MaxValue;
            int red = pixel.R;
            int green = pixel.G;
            int blue = pixel.B;
            int colorIndex = 0;
            // Loop through the entire palette, looking for the closest color match
            for (int index = 0; index < _colors.Length; index++)
            {
                // Lookup the color from the palette
                Color paletteColor = _colors[index];

                // Compute the distance from our source color to the palette color
                int redDistance = paletteColor.R - red;
                int greenDistance = paletteColor.G - green;
                int blueDistance = paletteColor.B - blue;

                int distance = (redDistance * redDistance) +
                                    (greenDistance * greenDistance) +
                                    (blueDistance * blueDistance);

                // If the color is closer than any other found so far, use it
                if (distance < leastDistance)
                {
                    colorIndex = (byte)index;
                    leastDistance = distance;

                    // And if it's an exact match, exit the loop
                    if (0 == distance)
                        break;
                }
            }

            return _colors[colorIndex];
        }
        bool isDragging = false;
        private void btnSymbol_Click(object sender, MouseEventArgs e)
        {
            Panel source = (Panel)sender;
            DoDragDrop(source.BackgroundImage,
                       DragDropEffects.Copy);
        }
        public PROGRAM_MODE currentMode = PROGRAM_MODE.TEXT;
        private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (tabControl1.SelectedIndex == 0)
                currentMode = PROGRAM_MODE.TEXT;
            else if (tabControl1.SelectedIndex == 1)
                currentMode = PROGRAM_MODE.PLAYLIST;
            else
                currentMode = PROGRAM_MODE.UNKNOW;
        }

        internal void populatePlaylist(PlaylistData playlistData)
        {
            if (playlistData.Info.PackageCount == 0) return;
            ImageList imageList = new ImageList();
            imageList.ImageSize = new Size(128, 128);
            imageList.ColorDepth = ColorDepth.Depth24Bit;
            Bitmap curImage;
            this.thumbnailListView.Items.Clear();
            for (int j = 0; j < playlistData.packages.Count; j++)
            {
                curImage = new Bitmap(128, 64);
                using (Graphics g = Graphics.FromImage(curImage))
                {
                    g.FillRectangle(PickBrush(), 0f, 0f, (float)curImage.Width, (float)curImage.Height);
                }
                images.Add(curImage);
                //Image img1 = ProcessInputImage(curImage);

                imageList.Images.Add(getThumbnaiImage(imageList.ImageSize.Width, curImage));
            }

            for (int j = 0; j < playlistData.packages.Count; j++)
            {
                this.thumbnailListView.Items.Add(playlistData.packages[j].Info.Index.ToString());
                this.thumbnailListView.Items[j].ImageIndex = j;
            }

            this.thumbnailListView.View = View.LargeIcon;
            this.thumbnailListView.LargeImageList = imageList;
        }

        
        internal void populatePlaylistNew(LedPlaylist playlist)
        {
            // check invalid input
            if (playlist == null || playlist.Count == 0) return;

            // create image list for thumbnail
            ImageList imageList = new ImageList();
            // set image size
            imageList.ImageSize = new Size(THUMBNAIL_SIZE, THUMBNAIL_SIZE);
            // set image color depth
            imageList.ColorDepth = ColorDepth.Depth24Bit;

            Bitmap curImage;
            images.Clear();
            this.thumbnailListView.Items.Clear();
            for (int j = 0; j < playlist.Count; j++)
            {
                curImage = playlist[j].CreateBitmapImage();
                images.Add(curImage);

                imageList.Images.Add(getThumbnaiImage(THUMBNAIL_SIZE, curImage));
            }

            for (int j = 0; j < playlist.Count; j++)
            {
                this.thumbnailListView.Items.Add(playlist[j].ID.ToString());
                this.thumbnailListView.Items[j].ImageIndex = j;
            }

            this.thumbnailListView.View = View.LargeIcon;
            this.thumbnailListView.LargeImageList = imageList;
        }

        Random rnd = new Random();
        private Brush PickBrush()
        {
            Brush result = Brushes.Transparent;

            Type brushesType = typeof(Brushes);

            System.Reflection.PropertyInfo[] properties = brushesType.GetProperties();

            int random = rnd.Next(properties.Length);
            result = (Brush)properties[random].GetValue(null, null);

            return result;
        }

        internal void populateProgram(BaseProgram baseProgram)
        {
            letiLed.initWithBitmap(
                ImageUtils.ResizeImage(
                    baseProgram.CreateBitmapImage(),
                    letiLed.Resolution.Width,
                    letiLed.Resolution.Height
                )
            );
        }

        private void thumbnailListView_DrawItem(object sender, DrawListViewItemEventArgs e)
        {
            // This is the default text alignment
            TextFormatFlags flags = TextFormatFlags.HorizontalCenter;
            e.DrawText(flags);
        }

        private void thumbnailListView_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
        {
            // Draw the column header normally
            e.DrawDefault = true;
            e.DrawBackground();
            e.DrawText();
        }
        List<Panel> overlayItems = new List<Panel>();

        private void btnToolMagic_Click(object sender, EventArgs e)
        {
            letiLed.CurrentTool = LETILed.TOOL.MAGIC;
        }

        private void btnToolText_Click(object sender, EventArgs e)
        {
            letiLed.CurrentTool = LETILed.TOOL.TEXT;
        }

        private void btnToolErase_Click(object sender, EventArgs e)
        {
            letiLed.CurrentTool = LETILed.TOOL.ERASE;
        }

        /*
        ObjectItemModel lastItemModel;
        private void letiLed_ObjectSelected(ObjectItemModel model)
        {
            if (model != null)
            {
                lastItemModel = model;
                lastItemModel.DataChanged += new ObjectItemModel.DataChangedHandler(model_DataChanged);
            }
            else
            {
                if (lastItemModel != null) 
                    lastItemModel.DataChanged -= model_DataChanged;
            }
            propertyGrid1.SelectedObject = lastItemModel;
        }
        */      

    }

    public enum PROGRAM_MODE
    {
        TEXT,
        PLAYLIST,
        UNKNOW
    }
}
